//! 系统监控相关的接口
use axum::Extension;
use sysinfo::{ProcessRefreshKind, ProcessesToUpdate};

use crate::{error::AppResult, extract::Json, state::AppState};

#[derive(serde::Serialize, serde::Deserialize)]
pub struct Cpu {
    core_num: Option<usize>,
    usage: f32,
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct Memory {
    total: u64,
    used: u64,
    free: u64,
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct App {
    pid: u32,
    run_time: u64,
    start_time: u64,
    cpu: f32,
    memory: u64,
    work_path: Option<std::path::PathBuf>,
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct System {
    os: Option<String>,
    host_name: Option<String>,
    arch: Option<String>,
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct MonitorServer {
    cpu: Cpu,
    memory: Memory,
    app: Option<App>,
    system: System,
    timestamp: u128,
}

static CACHE_KEY: &str = "monitor_server";

pub async fn monitor_server(
    Extension(state): Extension<AppState>,
) -> AppResult<Json<MonitorServer>> {
    let cache: Option<MonitorServer> = state
        .cache()
        .get(CACHE_KEY)
        .await
        .and_then(|s: String| serde_json::from_str(s.as_str()).ok());
    if let Some(cache) = cache {
        return Ok(Json(cache));
    }
    let mut sys = sysinfo::System::new_all();
    tokio::time::sleep(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await;
    sys.refresh_cpu_usage();
    let cpu = Cpu {
        core_num: sys.physical_core_count(),
        usage: sys.global_cpu_usage(),
    };
    sys.refresh_memory_specifics(sysinfo::MemoryRefreshKind::new().with_ram());
    let memory = Memory {
        total: sys.total_memory(),
        used: sys.used_memory(),
        free: sys.free_memory(),
    };
    let pid = std::process::id();
    let pid = sysinfo::Pid::from_u32(pid);
    sys.refresh_processes_specifics(
        ProcessesToUpdate::Some(&vec![pid]),
        ProcessRefreshKind::new().with_cpu().with_memory(),
    );
    let app = sys.process(pid).map(|p| App {
        pid: pid.as_u32(),
        run_time: p.run_time(),
        start_time: p.start_time(),
        memory: p.memory(),
        cpu: p.cpu_usage(),
        work_path: p.cwd().map(|f| f.to_owned()),
    });
    let system = System {
        os: sysinfo::System::name(),
        host_name: sysinfo::System::host_name(),
        arch: sysinfo::System::cpu_arch(),
    };
    let timestamp = std::time::SystemTime::now()
        .duration_since(std::time::SystemTime::UNIX_EPOCH)
        .map(|f| f.as_micros())
        .unwrap_or(0);
    let data = MonitorServer {
        cpu,
        memory,
        app,
        system,
        timestamp,
    };
    if let Ok(cache) = serde_json::to_string(&data) {
        let _ = state.cache().set_ex(CACHE_KEY, cache, 1800).await;
    }
    Ok(Json(data))
}
